package com.heima.article.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.*;
import com.heima.article.service.ApArticleService;
import com.heima.common.constants.article.ArticleConstants;
import com.heima.common.exception.CustomException;
import com.heima.feigns.behavior.BehaviorFeign;
import com.heima.feigns.user.UserFeign;
import com.heima.model.article.dto.ArticleDto;
import com.heima.model.article.dto.ArticleHomeDto;
import com.heima.model.article.dto.ArticleInfoDto;
import com.heima.model.article.mess.ArticleVisitStreamMess;
import com.heima.model.article.pojo.*;
import com.heima.model.article.vo.HotArticleVo;
import com.heima.model.behavior.dto.ApArticleRelationDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.threadlocal.AppThreadLocalUtils;
import com.heima.model.user.pojo.ApUser;
import com.heima.model.user.pojo.ApUserFollow;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle>
    implements ApArticleService {

  @Value("${file.oss.web-site}")
  String webSite;

  @Autowired AuthorMapper authorMapper;

  @Autowired ApArticleConfigMapper apArticleConfigMapper;

  @Autowired ApArticleContentMapper apArticleContentMapper;

  /**
   * 保存三种表的信息
   *
   * @param articleDto
   * @return
   */
  @Override
  public ResponseResult saveApArticle(ArticleDto articleDto) {
    // 1.检查参数
    // 2.将dto转化为article对象
    ApArticle apArticle = new ApArticle();

    BeanUtils.copyProperties(articleDto, apArticle);
    // 3.根据作者名称查询作者id
    ApAuthor author =
        authorMapper.selectOne(
            Wrappers.<ApAuthor>lambdaQuery().eq(ApAuthor::getName, articleDto.getAuthorName()));
    // 4.判断文件id是否存在
    if (author == null) {
      throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST, "作者信息不存在");
    }
    apArticle.setAuthorId(Long.valueOf(author.getId()));

    if (apArticle.getId() == null) {
      // 4.1如果id为空 保存文章
      apArticle.setLikes(0);
      apArticle.setComment(0);
      apArticle.setViews(0);
      apArticle.setCollection(0);
      save(apArticle);
    } else {
      // 4.2如果id不为空  修改文章
      updateById(apArticle);
      //  将之前的文章配置和文章内容删除
      apArticleConfigMapper.delete(
          Wrappers.<ApArticleConfig>lambdaQuery()
              .eq(ApArticleConfig::getArticleId, apArticle.getId()));
      apArticleContentMapper.delete(
          Wrappers.<ApArticleContent>lambdaQuery()
              .eq(ApArticleContent::getArticleId, apArticle.getId()));
    }
    // 5.统一保存  配执信息  和 文章内容信息
    ApArticleConfig config = new ApArticleConfig();
    config.setArticleId(apArticle.getId());
    config.setIsComment(true); // 可以评论
    config.setIsForward(true); // 可以转发
    config.setIsDown(false); // 未下架
    config.setIsDelete(false); // 未删除
    apArticleConfigMapper.insert(config);

    ApArticleContent content = new ApArticleContent();
    content.setArticleId(apArticle.getId());
    content.setContent(articleDto.getContent());
    apArticleContentMapper.insert(content);
    // 6.返回文章的id
    return ResponseResult.okResult(apArticle.getId());
  }

  @Autowired ApArticleMapper apArticleMapper;

  @Override
  public ResponseResult load(Short loadtype, ArticleHomeDto dto) {

    // 1.检查参数
    if (dto == null) {
      dto = new ArticleHomeDto();
    }
    Integer size = dto.getSize();
    if (size == null || size <= 0) {
      dto.setSize(10);
    }
    if (StringUtils.isBlank(dto.getTag())) {
      dto.setTag(ArticleConstants.DEFAULT_TAG);
    }
    if (dto.getMaxBehotTime() == null) {
      dto.setMaxBehotTime(new Date());
    }
    if (dto.getMinBehotTime() == null) {
      dto.setMinBehotTime(new Date());
    }
    // 2.调用mapper执行查询
    List<ApArticle> apArticles = apArticleMapper.loadArticleList(dto, loadtype);

    // 3.返回
    ResponseResult responseResult = ResponseResult.okResult(apArticles);
    responseResult.setHost(webSite);
    return responseResult;
  }


  @Autowired
  RedisTemplate<String,String> redisTemplate;

  /**
   * 根据参数加载文章列表  v2
   *
   * @param loadtypeLoadMore
   * @param dto
   * @param firstPage
   * @return
   */
    @Override
    public ResponseResult load2(Short loadtypeLoadMore, ArticleHomeDto dto, boolean firstPage) {
       //只要firstPage为true的话从redis里查,redis里没有就从以前的里面查
      if (firstPage){
        //从redis缓存中查询 热点文章数据
        String hotArticleVoListJson = redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            if (StrUtil.isNotEmpty(hotArticleVoListJson)){
              List<HotArticleVo> hotArticleVoList = JSONArray.parseArray(hotArticleVoListJson, HotArticleVo.class);
              ResponseResult responseResult = ResponseResult.okResult(hotArticleVoList);
              responseResult.setHost(webSite);
              return responseResult;
            }
      }

        return load(loadtypeLoadMore,dto);
    }

    @Override
  public ResponseResult loadArticleInfo(ArticleInfoDto dto) {
    // 1.检查参数
    if (dto == null) {
      throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
    }
    Long articleId = dto.getArticleId();

    ApArticleConfig apArticleConfig =
        apArticleConfigMapper.selectOne(
            Wrappers.<ApArticleConfig>lambdaQuery().eq(ApArticleConfig::getArticleId, articleId));
    if (apArticleConfig == null || apArticleConfig.getIsDown() || apArticleConfig.getIsDelete()) {
      return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章已下架");
    }

    ApArticleContent apArticleContent =
        apArticleContentMapper.selectOne(
            Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, articleId));

    if (apArticleContent == null) {
      ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
    }

    // 返回封装结果
    Map map = new HashMap<>();
    map.put("config", apArticleConfig);
    map.put("content", apArticleContent);

    return ResponseResult.okResult(map);
  }

  @Autowired
  private BehaviorFeign behaviorFeign;
  @Autowired
  private UserFeign userFeign;
  @Autowired
  ApCollectionMapper apCollectionMapper;
  /**
   * 加载文章详情的初始化配置信息，比如关注、喜欢、不喜欢等
   *
   * @param dto
   * @return
   */
    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {
      //1 参数校验
      if(dto == null || dto.getArticleId() == null || dto.getAuthorId() == null){
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
      }
      boolean iscollection=false,isfollow=false;

      ApUser user = AppThreadLocalUtils.getUser();
      // 是否关注
      ApAuthor author = authorMapper.selectById(dto.getAuthorId());
      if (author!=null){
        ApUserFollow apUserFollow = userFeign.findByUserIdAndFollowId(user.getId(), author.getUserId());
        if (apUserFollow!=null){
          isfollow=true;
        }
      }

      // 是否点赞/不喜欢
      ApArticleRelationDto relationDto = new ApArticleRelationDto();
      relationDto.setArticleId(dto.getArticleId());
    relationDto.setEntryId(user.getId());
      relationDto.setType((short)1);
      Map behaviorMap = behaviorFeign.findApArticleRelation(relationDto);

      // 收藏
      ApCollection apCollection = apCollectionMapper.selectOne(Wrappers.<ApCollection>lambdaQuery()
              .eq(ApCollection::getArticleId, dto.getArticleId())
              .eq(ApCollection::getEntryId, behaviorMap.get("entryId")));
    if (apCollection!=null){
      iscollection=true;
    }
      //3 返回结果
      behaviorMap.put("isfollow",isfollow);
      behaviorMap.put("iscollection",iscollection);
    return ResponseResult.okResult(behaviorMap);
    }

  /**
  /**
   * 重新计算文章分值
   * @param mess
   */
  @Override
  public void updateApArticle(ArticleVisitStreamMess mess) {
    //1.根据文章id 查询文章数据
    ApArticle article = this.getById(mess.getArticleId());
    //2.更新文章指定行为数据
    if (mess.getView()!=0){
      article.setViews(article.getViews()==null?(int)mess.getView():article.getViews()+(int)mess.getView());
    }
    if (mess.getLike()!=0){
      article.setLikes(article.getLikes()==null?(int)mess.getLike():article.getLikes()+(int)mess.getLike());
    }
    if (mess.getComment()!=0){
      article.setComment(article.getComment()==null?(int)mess.getComment():article.getComment()+(int)mess.getComment());
    }
    if (mess.getCollect()!=0){
      article.setCollection(article.getCollection()==null?(int)mess.getCollect():article.getCollection()+(int)mess.getCollect());
    }
    this.updateById(article);
    //3.按照权重计算文章分值,今日整体权重*3
    Integer scre = computeScre(article);
    scre=scre*3;
    //4.判断当前文章  在所属频道中  能否排行到热点文章前30 , 替换分值较低热点文章
    updateArticleCache(article,scre,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+article.getChannelId());
    //5.判断当前文章  在所有频道中  能否排行到热点文章前30 , 替换分值较低热点文章
    updateArticleCache(article,scre,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ArticleConstants.DEFAULT_TAG);

  }
  /**
   * 更新热点文章缓存
   * @param apArticle  待更新文章
   * @param score 该文章的热度分值
   * @param cacheKey  缓存的key
   */
  private void updateArticleCache(ApArticle apArticle, Integer score, String cacheKey){
    //1.查询redis中缓存的热点文章列表,redis中存的是json的
    String hotArticleListJson = redisTemplate.opsForValue().get(cacheKey);
    if (StrUtil.isEmpty(hotArticleListJson)){
      log.error("缓存热点文章  缓存失败");
      return;
    }
    List<HotArticleVo> hotArticleVoList = JSON.parseArray(hotArticleListJson, HotArticleVo.class);
    // 2.查看当前文章是否存在缓存中,如果存在  更新缓存中的score分数
    boolean isHas =false;
    for (HotArticleVo hotArticleVo : hotArticleVoList) {
      if (hotArticleVo.getId().equals(apArticle.getId())){
        hotArticleVo.setScore(score);
        isHas=true;
        break;
      }
    }
    //3.如果不存在,将当前文章封装成vo添加到 热点文章中
    if (!isHas){
      HotArticleVo hotArticleVo = new HotArticleVo();
      BeanUtils.copyProperties(apArticle,hotArticleVo);
      hotArticleVo.setScore(score);
      hotArticleVoList.add(hotArticleVo);
    }

    //4.将最终的热点文章集合  按照得分降序排序  取前30条文章
    hotArticleVoList = hotArticleVoList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
            .limit(30)
            .collect(Collectors.toList());

    //50.将新的30条文章重新缓存到redis中
    redisTemplate.opsForValue().set(cacheKey, JSONUtil.toJsonStr(hotArticleVoList));

  }
  //计算得分
  private Integer computeScre(ApArticle apArticle) {
    Integer score = 0;
    if (apArticle.getViews()!=null){
      score+=apArticle.getViews()* ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
    }
    if (apArticle.getLikes()!=null){
      score+=apArticle.getLikes()*ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
    }
    if (apArticle.getComment()!=null){
      score+=apArticle.getComment()*ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
    }
    if (apArticle.getCollection()!=null){
      score+=apArticle.getCollection()*ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
    }
    return score;
  }

}
